// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// This file contains the definition of the RingBuffer class.

#ifndef GPU_COMMAND_BUFFER_CLIENT_RING_BUFFER_H_
#define GPU_COMMAND_BUFFER_CLIENT_RING_BUFFER_H_

#include <stdint.h>

#include <deque>

#include "base/logging.h"
#include "base/macros.h"
#include "gpu/gpu_export.h"

namespace gpu {
class CommandBufferHelper;

// RingBuffer manages a piece of memory as a ring buffer. Memory is allocated
// with Alloc and then a is freed pending a token with FreePendingToken.  Old
// allocations must not be kept past new allocations.
class GPU_EXPORT RingBuffer {
public:
    typedef unsigned int Offset;

    // Creates a RingBuffer.
    // Parameters:
    //   alignment: Alignment for allocations.
    //   base_offset: The offset of the start of the buffer.
    //   size: The size of the buffer in bytes.
    //   helper: A CommandBufferHelper for dealing with tokens.
    //   base: The physical address that corresponds to base_offset.
    RingBuffer(unsigned int alignment, Offset base_offset,
        unsigned int size, CommandBufferHelper* helper, void* base);

    ~RingBuffer();

    // Allocates a block of memory. If the buffer is out of directly available
    // memory, this function may wait until memory that was freed "pending a
    // token" can be re-used.
    //
    // Parameters:
    //   size: the size of the memory block to allocate.
    //
    // Returns:
    //   the pointer to the allocated memory block.
    void* Alloc(unsigned int size);

    // Frees a block of memory, pending the passage of a token. That memory won't
    // be re-allocated until the token has passed through the command stream.
    //
    // Parameters:
    //   pointer: the pointer to the memory block to free.
    //   token: the token value to wait for before re-using the memory.
    void FreePendingToken(void* pointer, unsigned int token);

    // Discards a block within the ring buffer.
    //
    // Parameters:
    //   pointer: the pointer to the memory block to free.
    void DiscardBlock(void* pointer);

    // Gets the size of the largest free block that is available without waiting.
    unsigned int GetLargestFreeSizeNoWaiting();

    // Gets the total size of all free blocks that are available without waiting.
    unsigned int GetTotalFreeSizeNoWaiting();

    // Gets the size of the largest free block that can be allocated if the
    // caller can wait. Allocating a block of this size will succeed, but may
    // block.
    unsigned int GetLargestFreeOrPendingSize()
    {
        return size_;
    }

    // Gets a pointer to a memory block given the base memory and the offset.
    void* GetPointer(RingBuffer::Offset offset) const
    {
        return static_cast<int8_t*>(base_) + offset;
    }

    // Gets the offset to a memory block given the base memory and the address.
    RingBuffer::Offset GetOffset(void* pointer) const
    {
        return static_cast<int8_t*>(pointer) - static_cast<int8_t*>(base_);
    }

    // Rounds the given size to the alignment in use.
    unsigned int RoundToAlignment(unsigned int size)
    {
        return (size + alignment_ - 1) & ~(alignment_ - 1);
    }

private:
    enum State {
        IN_USE,
        PADDING,
        FREE_PENDING_TOKEN
    };
    // Book-keeping sturcture that describes a block of memory.
    struct Block {
        Block(Offset _offset, unsigned int _size, State _state)
            : offset(_offset)
            , size(_size)
            , token(0)
            , state(_state)
        {
        }
        Offset offset;
        unsigned int size;
        unsigned int token; // token to wait for.
        State state;
    };

    typedef std::deque<Block> Container;
    typedef unsigned int BlockIndex;

    void FreeOldestBlock();

    CommandBufferHelper* helper_;

    // Used blocks are added to the end, blocks are freed from the beginning.
    Container blocks_;

    // The base offset of the ring buffer.
    Offset base_offset_;

    // The size of the ring buffer.
    Offset size_;

    // Offset of first free byte.
    Offset free_offset_;

    // Offset of first used byte.
    // Range between in_use_mark and free_mark is in use.
    Offset in_use_offset_;

    // Alignment for allocations.
    unsigned int alignment_;

    // The physical address that corresponds to base_offset.
    void* base_;

    DISALLOW_IMPLICIT_CONSTRUCTORS(RingBuffer);
};

} // namespace gpu

#endif // GPU_COMMAND_BUFFER_CLIENT_RING_BUFFER_H_
